#pragma once
#include <memory>
#include <fstream>
#include <cassert>
#include <sstream>
#include <ctime>
#include "Util.hpp"

namespace mylog
{
    class Logsink
    {
    public:
        using ptr = std::shared_ptr<Logsink>;
        virtual ~Logsink() {};
        virtual void Log(const char *data, size_t len) = 0;
    };
    class StdoutSink : public Logsink
    {
    public:
        void Log(const char *data, size_t len)
        {
            std::cout.write(data, len);
        }
    };
    class FileSink : public Logsink
    {
    public:
        FileSink(const std::string &pathname) : _pathname(pathname)
        {
            FileUtil::CreateDirectory(FileUtil::Getpath(_pathname));
            _ofs.open(_pathname, std::ios::binary | std::ios::app);
            assert(_ofs.is_open());
        }
        void Log(const char *data, size_t len)
        {
            _ofs.write(data, len);
            assert(_ofs.good());
        }

    private:
        std::string _pathname;
        std::ofstream _ofs;
    };
    class RollsizeSink : public Logsink
    {
    public:
        RollsizeSink(const std::string &basename, size_t max_fsize) : 
        _basename(basename), 
        _max_fsize(max_fsize),
        _cur_fsize(0),
        _count(0)
        {
            std::string pathname = CreateNewfile();
            FileUtil::CreateDirectory(FileUtil::Getpath(pathname));
            _ofs.open(pathname, std::ios::binary | std::ios::app);
            assert(_ofs.is_open());
        }
        void Log(const char *data, size_t len)
        {
            if(_cur_fsize >= _max_fsize)
            {
                _ofs.close();
                std::string pathname = CreateNewfile();
                _ofs.open(pathname, std::ios::binary | std::ios::app);
                assert(_ofs.is_open());
                _cur_fsize = 0;
            }
            _ofs.write(data, len);
            assert(_ofs.is_open());
            _cur_fsize += len;
        }

    private:
        std::string CreateNewfile()
        {
            time_t t = DateUtil::Now();
            struct tm lm;
            localtime_r(&t, &lm);
            std::stringstream ss;
            ss << _basename;
            ss << lm.tm_year + 1900;
            ss << lm.tm_mon + 1;
            ss << lm.tm_mday;
            ss << lm.tm_hour;
            ss << lm.tm_min;
            ss << lm.tm_sec;
            ss << "-";
            ss << _count++;
            ss << ".log";
            return ss.str();
        }

    private:
        std::string _basename;
        std::ofstream _ofs;
        size_t _max_fsize;
        size_t _cur_fsize;
        size_t _count;
    };
    class SinkFactory
    {
    public:
        template<typename SinkFmt, typename ...Args>
        static Logsink::ptr CreateSink(Args&& ...args)
        {
            return std::make_shared<SinkFmt>(std::forward<Args>(args)...);
        }
    };
}